package com.akjava.gwt.markdowneditor.client;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.akjava.gwt.lib.client.GWTHTMLUtils;
import com.akjava.gwt.lib.client.LogUtils;
import com.akjava.gwt.lib.client.StorageControler;
import com.akjava.gwt.lib.client.StorageException;
import com.akjava.gwt.lib.client.TextSelection;
import com.akjava.gwt.lib.client.widget.TabInputableTextArea;
import com.akjava.lib.common.functions.StringFunctions;
import com.akjava.lib.common.predicates.StringPredicates;
import com.akjava.lib.common.tag.Tag;
import com.akjava.lib.common.utils.CSVUtils;
import com.akjava.lib.common.utils.TagUtil;
import com.akjava.lib.common.utils.ValuesUtils;
import com.google.common.base.Function;
import com.google.common.base.Joiner;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyUpEvent;
import com.google.gwt.event.dom.client.KeyUpHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.Panel;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SplitLayoutPanel;
import com.google.gwt.user.client.ui.TabLayoutPanel;
import com.google.gwt.user.client.ui.TextArea;
import com.google.gwt.user.client.ui.VerticalPanel;
public class MarkdownEditor extends SplitLayoutPanel {
public static final String KEY_SESSION="markdowneditor_session_value";//for session storage
public static final String KEY_LAST_SESSION_ID="markdowneditor_session_last_session_id";
//private static final String KEY_MARKDOWNEDITOR = "KEY_MARKDOWN_EDITOR";
private TextArea textArea;
public TextArea getTextArea() {
return textArea;
}
private CheckBox autoConvertCheck;
private HTML previewHTML;
private TextArea htmlArea;
public TextArea getHtmlArea() {
return htmlArea;
}
private ListBox titleLevelBox;
private StorageControler sessionControler=new StorageControler(false);//use session
private StorageControler localControler=new StorageControler(true);//use session
private ListBox imageListBox;
private Optional<String> syncHtmlKey=Optional.absent();
private Optional<String> syncTextKey=Optional.absent();
private TabLayoutPanel rightTabPanel;
private VerticalPanel previewPanel;
public void setSyncHtmlKey(Optional<String> syncHtmlKey) {
this.syncHtmlKey = syncHtmlKey;
}
public void setSyncTextKey(Optional<String> syncTextKey) {
this.syncTextKey = syncTextKey;
}
public MarkdownEditor(){
this(false,"","");
}
public static final String MD_EDITOR_COPIED_TEXT="system_copied_text";
public void copyText(String text) {
try {
localControler.setValue(MD_EDITOR_COPIED_TEXT, text);
} catch (StorageException e) {
Window.alert(e.getMessage());
e.printStackTrace();
}
}
public String getCopiedText(){
return localControler.getValue(MD_EDITOR_COPIED_TEXT,"");
}
/**
*
* @param readOnly
* @param session_id
* @param defaultValue
*
* HTML integration
* @see GWTMarkdownEditor
*/
public MarkdownEditor(boolean readOnly,String session_id,String defaultValue){
createLeftPanels();
createRightPanels();
/**
* what is doing?
* try to keep value when browser back
*
* session_id is usually get from html hidden-input.
* get KEY_LAST_SESSION_ID from storage.
*
* if these are equals,it's same session and action ;get value from storage.
* last edited text always store in storage.
*
* session_id is created when edit or add button clicked manually
*
*/
if(!session_id.isEmpty()){
try {
String lastSessionId = sessionControler.getValue(KEY_LAST_SESSION_ID, "");
GWT.log("gwtwiki:lastSessionId="+lastSessionId+",session_id="+session_id);
if(!session_id.equals(lastSessionId)){
//new situation
GWT.log("gwtwiki:different session id,get initial value from PEOPERTY_DEFAULT_ID");
//String data=ValueUtils.getFormValueById(PEOPERTY_DEFAULT_ID, "");
textArea.setText(defaultValue);
sessionControler.setValue(KEY_SESSION,defaultValue);
sessionControler.setValue(KEY_LAST_SESSION_ID, session_id);//mark used
}else{
GWT.log("gwtwiki:use last modified value");
String lastModified=sessionControler.getValue(KEY_SESSION, "");
textArea.setText(lastModified);
}
} catch (StorageException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
GWT.log("gwtwiki:no session id,get initial value from PEOPERTY_DEFAULT_ID");
//String data=ValueUtils.getFormValueById(PEOPERTY_DEFAULT_ID, "");
textArea.setText(defaultValue);
}
if(readOnly){
textArea.setReadOnly(readOnly);
}
doConvert();
}
public void setTotalHeightPX(int px){
this.setHeight(px+"px");
rightTabPanel.setHeight(px+"px");
// previewPanel.setHeight(px+"px");
// htmlArea.setHeight(px+"px");
textArea.setHeight((px-80)+"px");
}
private void createRightPanels() {
DockLayoutPanel rightPanel=new DockLayoutPanel(Unit.PX);
rightPanel.setSize("100%", "100%");
this.add(rightPanel);
createOptionArea(rightPanel);
rightTabPanel = new TabLayoutPanel(40,Unit.PX);
rightPanel.add(rightTabPanel);
rightTabPanel.setSize("100%","100%");
createPreviewArea(rightTabPanel);
createHtmlArea(rightTabPanel);
rightTabPanel.selectTab(0);
}
public TabLayoutPanel getRightTabPanel(){
return rightTabPanel;
}
private void createPreviewArea(TabLayoutPanel tab) {
previewPanel = new VerticalPanel();//really need?
previewPanel.setSize("100%","100%");
ScrollPanel scroll=new ScrollPanel();
scroll.setWidth("100%");
previewPanel.add(scroll);
scroll.setHeight("100%");
previewHTML = new HTML();
//scroll.setWidget(previewHTML);
scroll.setWidget(htmlContainer);
tab.add(previewPanel,"Preview");
}
private void createHtmlArea(TabLayoutPanel tab) {
VerticalPanel container=new VerticalPanel();
container.setSize("100%", "100%");
htmlArea = new TextArea();
htmlArea.setWidth("95%");
htmlArea.setHeight("100%");
container.add(htmlArea);
tab.add(container,"HTML");
}
private void createLeftPanels() {
DockLayoutPanel leftPanel=new DockLayoutPanel(Unit.PX);
leftPanel.setSize("100%", "100%");
this.addWest(leftPanel,560);
createToolbars(leftPanel);
createTextAreas(leftPanel);
}
private void createOptionArea(DockLayoutPanel parent) {
HorizontalPanel panel=new HorizontalPanel();
parent.addNorth(panel,40);
Button bt=new Button("Convert");
bt.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
doConvert();
}
});
panel.add(bt);
autoConvertCheck = new CheckBox("auto");
autoConvertCheck.setValue(true);
autoConvertCheck.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if(autoConvertCheck.getValue()){
onTextAreaUpdate();
}
}
});
panel.add(autoConvertCheck);
}
private void createTextAreas(Panel parent) {
//VerticalPanel textAreaBase=new VerticalPanel();
//textAreaBase.setSize("100%", "100%");
//parent.add(textAreaBase);
//textAreaBase.setBorderWidth(3);
textArea = new TabInputableTextArea();
parent.add(textArea);
//textArea.setText(storageControler.getValue(KEY_MARKDOWNEDITOR, ""));
textArea.setStylePrimaryName("textbg");
textArea.setWidth("98%");
textArea.setHeight("98%");
textArea.addKeyUpHandler(new KeyUpHandler() {
@Override
public void onKeyUp(KeyUpEvent event) {
if(event.getNativeKeyCode()==KeyCodes.KEY_ENTER){
onTextAreaUpdate();
}
else if(event.isControlKeyDown()){//copy or paste
onTextAreaUpdate();
}
else{
onTextAreaUpdate();
}
}
});
/*
* these called when focus out
textArea.addValueChangeHandler(new ValueChangeHandler<String>() {
@Override
public void onValueChange(ValueChangeEvent<String> event) {
LogUtils.log("value-changed");
}
});
textArea.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
LogUtils.log("changed");
}
});
*/
}
private void createToolbars(DockLayoutPanel parent) {
VerticalPanel panels=new VerticalPanel();
//panels.setHeight("100px");
parent.addNorth(panels,60);
HorizontalPanel button1Panel=new HorizontalPanel();
panels.add(button1Panel);
HorizontalPanel button2Panel=new HorizontalPanel();
panels.add(button2Panel);
titleLevelBox = new ListBox();
titleLevelBox.addItem("Clear");
titleLevelBox.addItem("Head 1");
titleLevelBox.addItem("Head 2");
titleLevelBox.addItem("Head 3");
titleLevelBox.addItem("Head 4");
titleLevelBox.addItem("Head 5");
titleLevelBox.addItem("Head 6");
titleLevelBox.addItem("<HEAD>");
titleLevelBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
int index=titleLevelBox.getSelectedIndex();
if(index>6){
index=0;
}
titleSelected(index);
}
});
titleLevelBox.setTitle("convert pointed line to title");
titleLevelBox.setSelectedIndex(7);//default empty
button2Panel.add(titleLevelBox);
Button boldBt=new Button("Bold",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
insertBetweenSelectionText(selection,"**","**");
onTextAreaUpdate();
}
}
});
button1Panel.add(boldBt);
Button ItalicBt=new Button("Italic",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
insertBetweenSelectionText(selection,"*","*");
onTextAreaUpdate();
}
}
});
button1Panel.add(ItalicBt);
Button strikeBt=new Button("Strike",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
insertBetweenSelectionText(selection,"~~","~~");
onTextAreaUpdate();
}
}
});
strikeBt.setTitle("insert strike");
button1Panel.add(strikeBt);
Button codeBt=new Button("Code",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
if(selection.containLineBreak()){
insertBetweenSelectionText(selection,"```\n","\n```");
}else{
insertBetweenSelectionText(selection,"`","`");
}
onTextAreaUpdate();
}
}
});
codeBt.setTitle("insert code");
button1Panel.add(codeBt);
Button blockBt=new Button("Block",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
insertBetweenSelectionText(selection.getCurrentLine(),">","");
onTextAreaUpdate();
}
}
});
blockBt.setTitle("Add a Blockquote");
button1Panel.add(blockBt);
Button lineBt=new Button("HR",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
insertBetweenSelectionText(selection.getCurrentLine(),"********\n","");
onTextAreaUpdate();
}
}
});
lineBt.setTitle("Insert a horizontail Line");
button1Panel.add(lineBt);
Button LinkBt=new Button("URL",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
String url=Window.prompt("Link URL", "http://");
if(url==null){//cancel
return;
}
String newText="["+selected+"]"+"("+url+")";
selection.replace(newText);
onTextAreaUpdate();
}
}
});
LinkBt.setTitle("Insert a URL");
button1Panel.add(LinkBt);
Button alinkBt=new Button("Atag",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
String url=Window.prompt("Link URL", "http://");
if(url==null){//cancel
return;
}
String newText="<a href='"+url+"'>"+selected+"</a>";
selection.replace(newText);
onTextAreaUpdate();
}
}
});
alinkBt.setTitle("Insert a URL with alink");
button1Panel.add(alinkBt);
Button ImageBt=new Button("IMG",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
if(selected.endsWith(".jpg")||selected.endsWith(".png")){
List<String> newLines=new ArrayList<String>();
String[] lines=selected.split("\n");
for(String line:lines){
if(line.isEmpty()){
continue;
}
String newText="!["+""+"]"+"("+line+")";
newLines.add(newText);
}
selection.replace(Joiner.on("\n\n").join(newLines));
onTextAreaUpdate();
}else{
String url=Window.prompt("Image URL", "");
if(url==null){//cancel
return;
}
String newText="!["+selected+"]"+"("+url+")";
selection.replace(newText);
onTextAreaUpdate();
}
}
}
});
ImageBt.setTitle("Insert a Image");
button1Panel.add(ImageBt);
Button ListBt=new Button("List",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
List<String> converted=FluentIterable
.from(Arrays.asList(selected.split("\n")))
.filter(StringPredicates.getNotEmpty())
.transform(new StringFunctions.StringToPreFixAndSuffix("- ",""))
.toList();
selection.replace(Joiner.on("\n").join(converted));
onTextAreaUpdate();
}
}
});
ListBt.setTitle("Convert to List");
button1Panel.add(ListBt);
Button numberListBt=new Button("Nist",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
List<String> converted=FluentIterable
.from(Arrays.asList(selected.split("\n")))
.filter(StringPredicates.getNotEmpty())
.transform(new StringFunctions.StringToPreFixAndSuffixWithLineNumber(1,"${value}. ",""))
.toList();
selection.replace(Joiner.on("\n").join(converted));
onTextAreaUpdate();
}
}
});
numberListBt.setTitle("Convert to Number List");
button1Panel.add(numberListBt);
Button nBt=new Button("N",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
if(!selection.getCurrentLine().getSelection().endsWith(" ")){
String line=selection.getCurrentLine().getLineEndRemovedSelection();
if(!line.endsWith(" ")){
selection.getCurrentLine().replaceInLine(line+" ");
}
onTextAreaUpdate();
}
}
}
});
nBt.setTitle("two space line");
button1Panel.add(nBt);
Button tableBt=new Button("Tab2Table",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
List<List<String>> csvs=CSVUtils.csvToListList(selected, true, false);
Joiner tableJoiner=Joiner.on("|");
List<String> converted=new ArrayList<String>();
for(int i=0;i<csvs.size();i++){
List<String> csv=csvs.get(i);
converted.add(tableJoiner.join(csv));
if(i==0){//header need line
List<String> lines=new ArrayList<String>();
for(int j=0;j<csv.size();j++){
lines.add("---");
}
converted.add(tableJoiner.join(lines));
}
}
selection.replace(Joiner.on("\n").join(converted));
onTextAreaUpdate();
}
}
});
tableBt.setTitle("Convert Tab to table");
button2Panel.add(tableBt);
Button t2tableBt=new Button("Tab2Head",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
List<String> converted=FluentIterable
.from(Arrays.asList(selected.split("\n")))
.transform(new TabTitleFunction())
.toList();
selection.replace(Joiner.on("\n").join(converted));
onTextAreaUpdate();
}
}
});
t2tableBt.setTitle("Convert tab tree to Title");
button2Panel.add(t2tableBt);
Button tab2listBt=new Button("Tab2List",new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String selected=selection.getSelection();
List<String> converted=FluentIterable
.from(Arrays.asList(selected.split("\n")))
.transform(new TabListFunction())
.toList();
selection.replace(Joiner.on("\n").join(converted));
onTextAreaUpdate();
}
}
});
tab2listBt.setTitle("Convert tab tree to List");
button2Panel.add(tab2listBt);
imageListBox = new ListBox();
imageListBox.addItem("");
imageListBox.addItem("16");
imageListBox.addItem("32");
imageListBox.addItem("50");
imageListBox.addItem("64");
imageListBox.addItem("100");
imageListBox.addItem("200");
imageListBox.addItem("400");
imageListBox.addItem("600");
imageListBox.addItem("800");
imageListBox.addItem("<img>");
imageListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
int width=ValuesUtils.toInt(imageListBox.getItemText(imageListBox.getSelectedIndex()),0);
imageSelected(width);
}
});
imageListBox.setTitle("insert <img> tag with width");
imageListBox.setSelectedIndex(imageListBox.getItemCount()-1);//default empty
button2Panel.add(imageListBox);
button2Panel.add(new Button("Undo", new ClickHandler() {//TODO better
@Override
public void onClick(ClickEvent event) {
int undoIndex=historyIndex-1;
if(undoIndex<0){
return;
}
String text=textHistory.get(undoIndex);
textArea.setText(text);
lastHistory=text;
historyIndex--;
if(historyIndex<0){
historyIndex=0;
}
onTextAreaUpdate();
}
}));
button2Panel.add(new Button("Redo", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if(historyIndex<textHistory.size()){
int redoIndex=historyIndex+1;
if(redoIndex>=textHistory.size()){
return;
}
String text=textHistory.get(redoIndex);
textArea.setText(text);
lastHistory=text;
historyIndex++;
if(historyIndex>=textHistory.size()){
historyIndex=textHistory.size()-1;
}
onTextAreaUpdate();
}
}
}));
button2Panel.add(new Button("Paste", new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
for(TextSelection selection:TextSelection.createTextSelection(textArea).asSet()){
String copiedText=getCopiedText();
if(!Strings.isNullOrEmpty(copiedText)){
insertBetweenSelectionText(selection,copiedText,"");
onTextAreaUpdate();
}
}
}
}));
}
public void clearHistory(){
lastHistory=null;
textHistory.clear();
historyIndex=0;
}
public void addHistory(String text){
if(text.equals(lastHistory)){
return;
}
textHistory.add(text);
lastHistory=text;
//GWT.log("add history:"+text);
if(textHistory.size()>1000){
textHistory.remove(0);
}
historyIndex=textHistory.size()-1;
}
private List<String> textHistory=new ArrayList<String>();
private int historyIndex;
private String lastHistory;
protected void imageSelected(int width) {
String url=Window.prompt("Image URL", "");
if(url==null){//cancel
return;
}
Optional<TextSelection> selection= getTextSelection();
for(TextSelection textSelection:selection.asSet()){
Tag tag=new Tag("img").attr("src",url).attr("alt",textSelection.getSelection()).attr("width", ""+width).single();
textSelection.replace(tag.toString());
}
onTextAreaUpdate();
imageListBox.setSelectedIndex(imageListBox.getItemCount()-1);
}
public static class TabTitleFunction implements Function<String,String>{
@Override
public String apply(String input) {
if(input.isEmpty()){
return "";
}
String text="";
int level=1;//all text add level
for(int i=0;i<input.length();i++){
if(input.charAt(i)=='\t'){
level++;
}else{
text=input.substring(i);
break;
}
}
level=Math.min(level, 6);
return Strings.repeat("#", level)+text;
}
}
public static class TabListFunction implements Function<String,String>{
@Override
public String apply(String input) {
if(input.isEmpty()){
return "";
}
String text="";
int level=0;//all text add level
for(int i=0;i<input.length();i++){
if(input.charAt(i)=='\t'){
level++;
}else{
text=input.substring(i);
break;
}
}
//level=Math.min(level, 6);
return Strings.repeat("\t", level)+"- "+text;
}
}
private void debug(String text){
for(int i=0;i<text.length();i++){
LogUtils.log(i+":"+text.charAt(i)+","+((int)text.charAt(i)));
}
}
public static void insertBetweenSelectionText(TextSelection selection,String header,String footer){
String newText=header+selection.getSelection()+footer;
selection.replace(newText);
TextArea target=selection.getTargetTextArea();
target.setCursorPos(selection.getStart()+(header+selection.getSelection()).length());
target.setFocus(true);
}
private void titleSelected(int level){
Optional<TextSelection> selection= getTextSelection();
for(TextSelection textSelection:selection.asSet()){
LogUtils.log("select:"+textSelection.getSelection()+","+textSelection.getStart()+","+textSelection.getEnd());
TextSelection tmp1=textSelection.getCurrentLine();
LogUtils.log("current:"+tmp1.getSelection()+","+tmp1.getStart()+","+tmp1.getEnd());
TextSelection lineSelection=textSelection.getCurrentLine();
boolean startWithTitle=MarkdownPredicates.getStartWithTitleLinePredicate().apply(lineSelection.getSelection());
if(startWithTitle){
//can ignore next line
String newLine=MarkdownFunctions.getConvertTitle(level).apply(lineSelection.getSelection());
lineSelection.replace(newLine);
}else{
boolean nextLineIsTitle=false;
Optional<TextSelection> nextLine=lineSelection.getNextLine();
if(nextLine.isPresent()){
TextSelection tmp2=nextLine.get();
LogUtils.log("next:"+tmp2.getSelection()+","+tmp2.getStart()+","+tmp2.getEnd());
TextSelection nextLineSelection=nextLine.get();
nextLineIsTitle=MarkdownPredicates.getTitleLinePredicate().apply(nextLineSelection.getSelection());
}else{
LogUtils.log("no next-line");
}
String newLine=MarkdownFunctions.getConvertTitle(level).apply(lineSelection.getSelection());
if(nextLineIsTitle){
TextSelection bothSelection=new TextSelection(lineSelection.getStart(), nextLine.get().getEnd(), textArea);
bothSelection.replace(newLine);
}else{
lineSelection.replace(newLine);
}
}
}
onTextAreaUpdate();
titleLevelBox.setSelectedIndex(7);
}
public Optional<TextSelection> getTextSelection(){
return TextSelection.createTextSelection(textArea);
}
public void onTextAreaUpdate(){
addHistory(textArea.getText());//full backup
if(autoConvertCheck.getValue()){
doConvert();
}else{
}
syncOutput();//sync storage or html/text for form
}
/**
* syncOutput is integrate for standard html web-apps
*/
public void syncOutput(){//TODO async
//set values for html-form hidden
if(syncHtmlKey.isPresent()){
String html=htmlArea.getText();
GWTHTMLUtils.setValueAttributeById(syncHtmlKey.get(), html);
}
String text=textArea.getText();
if(syncTextKey.isPresent()){
GWTHTMLUtils.setValueAttributeById(syncTextKey.get(), text);
}
//store to session-storage for back-button
try {
sessionControler.setValue(KEY_SESSION, textArea.getText());
} catch (StorageException e) {
e.printStackTrace();
}
if(syncOutputListener!=null){
syncOutputListener.syncOutput(textArea.getText());
}
}
SyncOutputListener syncOutputListener;
public SyncOutputListener getSyncOutputListener() {
return syncOutputListener;
}
public void setSyncOutputListener(SyncOutputListener syncOutputListener) {
this.syncOutputListener = syncOutputListener;
}
public static interface SyncOutputListener{
public void syncOutput(String text);
}
public String getSessionValue(){
return sessionControler.getValue(KEY_SESSION,"");
}
public String getHtmlText(){
return htmlArea.getText();
}
public String getMarkdownText(){
return textArea.getText();
}
public String getHeader(){
return "";
}
public String getFooter(){
return "";
}
public void doConvert() {
String text=textArea.getText();
String html=Marked.marked(text);
htmlArea.setText(html);
//kill meta
int index=html.indexOf("<meta");
if(index!=-1){
int end=html.indexOf(">",index+1);
if(end!=-1){
html=html.substring(0,index)+"<"+html.substring(index+1,end)+"/>"+html.substring(end+1);
}
}
htmlContainer.clear();
List<Image> usedList=new ArrayList<Image>();
List<String> htmls=splitHtml(getHeader()+html+getFooter());
for(String tag:htmls){
if(tag.startsWith("<img ")){
//HTML imgHtml=htmlMap.get(ht);
Map<String,String> attr=TagUtil.getAttribute(tag);
String src=attr.get("src");
//we don't need care alt ,this is just preview
if(src!=null){
List<Image> cacheList=Lists.newArrayList(imageMap.get(src));
Image img=null;
for(Image cacheImage:cacheList){
if(!usedList.contains(cacheImage)){
img=cacheImage;
break;
}
}
if(img==null){
img=new Image(src);
imageMap.put(src, img);
}
usedList.add(img);
htmlContainer.add(img);
}else{
LogUtils.log("invalid image:"+tag);
}
}else{
htmlContainer.add(new HTML(tag));
}
}
//previewHTML.getElement().setInnerHTML(getHeader()+html+getFooter());
//previewHTML.setHTML(getHeader()+html+getFooter());
}
VerticalPanel htmlContainer=new VerticalPanel();
private List<String> splitHtml(String html){
List<String> htmls=new ArrayList<String>();
int index=html.indexOf("<img ");
do{
if(index==-1){
break;
}
String sub=html.substring(0,index);
htmls.add(sub);
int end=html.indexOf(">", index);
if(end==-1){
html=html.substring(index);//invalid text
break;
}else{
//check before
String before=sub.substring(Math.max(0, sub.length()-"<p>".length()));
String after=html.substring(end+1,Math.min(end+1+"</p>".length(),html.length()));
if(!before.equals("<p>")){
htmls.add("<b>Insert Line separator before image<b>");
}
String img=html.substring(index,end+1);
htmls.add(img);
html=html.substring(end+1);
if(!after.equals("</p>")){
htmls.add("<b>Insert Line separator after image<b>");
}
}
index=html.indexOf("<img ");
}while(index!=-1);
//add remain
htmls.add(html);
return htmls;
}
private Multimap<String,Image> imageMap=HashMultimap.create();
public boolean validHtmlHeader(String header){
String lower=header.toLowerCase();
if(lower.indexOf("<body")==-1){
return false;
}
if(lower.indexOf("<head")==-1){
return false;
}
if(lower.indexOf("</head>")==-1){
return false;
}
if(lower.indexOf("<html")==-1){
return false;
}
return true;
}
public boolean validHtmlFooter(String header){
String lower=header.toLowerCase();
if(lower.indexOf("</body>")==-1){
return false;
}
if(lower.indexOf("</html>")==-1){
return false;
}
return true;
}
public HTML getPreviewHTML() {
return previewHTML;
}
}